import pyinotify
import logging
import asyncore
import os
from . import socket_clients

class Watcher(object):

  def __init__(self, wm):
    self.wm = wm
    self.affected = set()
    self.locked = False
    self.watches = set()
    self.dieset = set()
    self.last_affected = set()

  def affect(self, fname):
    if fname in self.watches:
      self.affected.add(fname)
      self.watches.remove(fname)

  def deleted(self, fname):
    if fname in self.dieset:
      exit(0)
    self.affect(fname)

  def ack_affected(self):
    self.last_affected = set()

  def get_affected(self):
    for fname in self.affected:
      self.watch(fname)
    if self.last_affected:
      self.affected |= self.last_affected
    self.last_affected = self.affected
    affected = list(self.affected)
    self.affected = set()
    return affected

  def drop_changes(self, fname):
    if fname in self.affected:
      self.affected.remove(fname)
      return fname

  def watch(self, fname):
    self.watches.add(fname)
    #print("watch: " + repr(fname))
    watch = self.wm.add_watch(os.path.dirname(fname), pyinotify.ALL_EVENTS)
    if(fname in watch):
      self.watches[fname] = watch[fname]

  def unwatch(self, fname):
    if fname in self.watches:
      #print("unwatch: " + repr(fname))
      self.watches.discard(fname)
    else:
      print("no such watch: " + repr(fname))

  def config_dieset(self, dieset):
    self.dieset = dieset
    for fname in dieset:
      print("adding " + fname + " to diesset")
      self.wm.add_watch(os.path.dirname(fname), pyinotify.IN_DELETE)

class InotifyHook(pyinotify.ProcessEvent):

  def __init__(self, wm):
    self.wm = wm
    super().__init__()

  def process_IN_MODIFY(self, raw_event):
    self.wm.affect(raw_event.path)
    print(repr(raw_event))

  def process_NeedToWorryAbout(self, raw_event):
    if(raw_event.path == "."):
      fname = raw_event.name
    else:
      fname = raw_event.path + "/" + raw_event.name
    print(str(raw_event.maskname) + "(" + fname + ")")
    self.wm.affect(fname)

  def process_IN_DELETE(self, raw_event):
    if(raw_event.path == "."):
      fname = raw_event.name
    else:
      fname = raw_event.path + "/" + raw_event.name
    print(str(raw_event.maskname) + "(" + fname + ")")
    self.wm.deleted(fname)


  process_IN_MOVED_TO = process_NeedToWorryAbout
  process_IN_MOVED_FROM = process_NeedToWorryAbout
  process_IN_CLOSE_WRITE = process_NeedToWorryAbout
  process_IN_MODIFY = process_NeedToWorryAbout

def start_server(started_cmd = None, dieset = set()):
  """ 
  Args:
    started None callback() once server is started 
    dieset set.<str> set of files to kill the server if they change.
  """

  # Instanciate a new WatchManager (will be used to store watches).
  wm = pyinotify.WatchManager()
  # Associate this WatchManager with a Notifier (will be used to report and
  # process events).
  pyinotify.log.setLevel(logging.ERROR)
          
  watcher = Watcher(wm)

  pyinotify.AsyncNotifier(wm, InotifyHook(watcher))

  server = socket_clients.AffectedServer(watcher)

  if started_cmd:
    started_cmd()

  watcher.config_dieset(dieset)

  asyncore.loop()

